package com.citycloud.ccuap.tc.admin.config;

import com.citycloud.ccuap.tc.admin.admininterface.IAdminInterfaceService;
import com.citycloud.ccuap.tc.admin.entity.OauthClientDetails;
import com.citycloud.ccuap.tc.admin.entity.SysAdminInterface;
import com.citycloud.ccuap.tc.admin.repository.OauthClientDetailsRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Component
@Slf4j
public class DynamicAccessDecisionManager implements AccessDecisionManager {

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private OauthClientDetailsRepository oauthClientDetailsRepository;

    @Autowired
    private IAdminInterfaceService adminInterfaceService;

    @Autowired
    private SecurityConfig.SecurityOauth2ClientConfig clientConfig;

    private List<String> excludePathPatternList = new ArrayList<>();

    @PostConstruct
    public void init() {
        excludePathPatternList.add("/js/CodeData.js");
        excludePathPatternList.add("/js/CodeConfigData.js");

        excludePathPatternList.add("/get_token_by_code");

        excludePathPatternList.add("/v1/sys/parameter/query");
        excludePathPatternList.add("/v1/sys/user4sec/getAllSysProjects");
    }

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {

        FilterInvocation filterInvocation = (FilterInvocation) object;
        HttpServletRequest request = filterInvocation.getRequest();

        String requestMethod = request.getMethod();
        String requestUrl = request.getServletPath();
        log.info("OAuth2动态权限过滤：请求方式 -- {}，请求路径 -- {}", requestMethod, requestUrl);

        // 不过滤的请求直接放行
        for (String pathPattern : excludePathPatternList) {
            if (antPathMatcher.match(pathPattern, requestUrl)) {
                log.info("OAuth2动态权限过滤：忽略请求 -- {}，匹配规则 -- {}", requestUrl, pathPattern);
                return;
            }
        }
        if(authentication instanceof AnonymousAuthenticationToken){
            return;
        }

        // 获取client_id
        String clientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();
        log.info("OAuth2动态权限过滤：请求应用 -- {}", clientId);

        // 请求来自权限应用时不过滤
        if (clientConfig.getClientId().equalsIgnoreCase(clientId)) {
            return;
        }

        // 根据client获取其资源对应的菜单
        OauthClientDetails details = oauthClientDetailsRepository.findByClientId(clientId);
        String resourceIds = details.getResourceIds();


        if (StringUtils.isNotBlank(resourceIds)) {
            // 校验
            List<SysAdminInterface> list = adminInterfaceService.getClientInterfaces(resourceIds);
            for (SysAdminInterface sysAdminInterface : list) {
                if (antPathMatcher.match(sysAdminInterface.getInterfaceUrl(), requestUrl)) {
                    log.info("OAuth2动态权限过滤：匹配应用功能 -- {}（{}）", sysAdminInterface.getInterfaceName(), sysAdminInterface.getInterfaceUrl());
                    return;
                }
            }
        }
        throw new AccessDeniedException("无接口访问权限，请联系管理员！");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}